package org.itzixi.netty.websocket;

import com.a3test.component.idworker.IdWorkerConfigBean;
import com.a3test.component.idworker.Snowflake;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.itzixi.enums.MsgTypeEnum;
import org.itzixi.grace.result.GraceJSONResult;
import org.itzixi.netty.mq.MessagePublisher;
import org.itzixi.netty.utils.JedisPoolUtils;
import org.itzixi.netty.utils.ZookeeperRegister;
import org.itzixi.pojo.netty.ChatMsg;
import org.itzixi.pojo.netty.DataContent;
import org.itzixi.pojo.netty.NettyServerNode;
import org.itzixi.utils.JsonUtils;
import org.itzixi.utils.LocalDateUtils;
import org.itzixi.utils.OkHttpUtil;
import redis.clients.jedis.Jedis;

import java.time.LocalDateTime;

import static org.itzixi.utils.JsonUtils.jsonToPojo;

/**
 * 创建自定义助手类
 */
//SimpleChannelInboundHandler:对于请求来说相当于入站
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    // 用于记录和管理所有客户端的channel组
    public static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        // 获得客户端传输过来的消息
        String content = msg.text();
        System.out.println("接收到的数据"+content);

        // 1.获取客户端发来的消息并且解析
        DataContent dataContent = jsonToPojo(content, DataContent.class);
        ChatMsg chatMsg = dataContent.getChatMsg();
        String msgText = chatMsg.getMsg();
        String senderId = chatMsg.getSenderId();
        String receiverId = chatMsg.getReceiverId();

        // 判断是否黑名单 start
        // 双方有一方是黑名单，则不允许发送
        GraceJSONResult result = OkHttpUtil.get("http://127.0.0.1:1000/friendship/isBlack?friendId1st=" + receiverId
                                                                                        + "&friendId2nd=" + senderId);
        boolean isBlack = (boolean) result.getData();
        System.out.println("当前黑名单关系为 ： " + isBlack);
        if (isBlack) {
            return;
        }
        // 判断是否黑名单 end


        // 时间校准，以服务器时间为准
        chatMsg.setChatTime(LocalDateTime.now());
        Integer msgType = chatMsg.getMsgType();

        // 获取chanel
        Channel currentChannel = ctx.channel();
        String currentChannelId = currentChannel.id().asLongText();
        String currentChannelIdShort = currentChannel.id().asShortText();

//        System.out.println("当前chanel的id = " + currentChannelId + ", 简短id = " + currentChannelIdShort);
//        currentChannel.writeAndFlush(new TextWebSocketFrame(currentChannelId));


        // 2.判断消息类型，根据不同的类型来处理不同的业务
        if (msgType == MsgTypeEnum.CONNECT_INIT.type) {
            //当websocket初次连接的时候，初始化channel，把用户的channel和用户id关联起来
            UserChannelSession.putMultiChannels(senderId, currentChannel);
            UserChannelSession.putUserChannelIdRelation(currentChannelId, senderId);

            // 初次连接后，该节点的在线人数加1
            NettyServerNode minNode = dataContent.getServerNode();
            ZookeeperRegister.incrementOnlineCounts(minNode);

            // 获得IP和端口，在redis中设置关系，以便在前端设备断线后减少在线人数
            Jedis jedis = JedisPoolUtils.getJedis();
            jedis.set(senderId, JsonUtils.objectToJson(minNode));


        } else if (msgType == MsgTypeEnum.WORDS.type
                || msgType == MsgTypeEnum.IMAGE.type
                || msgType == MsgTypeEnum.VIDEO.type
                || msgType == MsgTypeEnum.VOICE.type) {


            // 此处为mq异步解耦，保存信息到数据库，数据库无法获得信息的主键id，使用snowflake直接生成主键id
            Snowflake snowflake = new Snowflake(new IdWorkerConfigBean());
            String sid = snowflake.nextId();
            System.out.println("使用snowflake生成的主键id = " + sid);

            String iid = IdWorker.getIdStr();
            System.out.println("使用mybatis-plus的idworker生成的主键id = " + iid);

            // 设置消息的主键id
            chatMsg.setMsgId(sid);


            // 发送消息
//            List<Channel> receiverChannels = UserChannelSession.getMultiChannels(receiverId);
//            if(receiverChannels == null || receiverChannels.size() == 0 || receiverChannels.isEmpty()) {
//                // 说明用户离线
//                chatMsg.setIsReceiverOnLine(false);
//            } else {
                // 说明用户在线
//                chatMsg.setIsReceiverOnLine(true);
                //遍历接收者的多个channel，进行消息推送
//                for (Channel c : receiverChannels) {
//                    Channel findChannel = clients.find(c.id());
//                    if (findChannel != null) {
                        //如果是语音，未读状态设置为false
                        if (msgType == MsgTypeEnum.VOICE.type) {
                            chatMsg.setIsRead(false);
                        }
                        dataContent.setChatMsg(chatMsg);
                        String chatTimeFormat = LocalDateUtils
                                .format(chatMsg.getChatTime(), LocalDateUtils.DATETIME_PATTERN_2);
                        dataContent.setChatTime(chatTimeFormat);
                        dataContent.setExtend(currentChannelId);
                        // 把聊天信息作为mq消息进行广播
                        MessagePublisher.sendMsgToNettyServers(JsonUtils.objectToJson(dataContent));
                        // 发送消息给在线的用户
//                        findChannel.writeAndFlush(new TextWebSocketFrame(JsonUtils.objectToJson(dataContent)));
//                    }
//                }
//            }
            // 把聊天信息作为mq的消息发送给消费者进行消费（保存到数据库）


            MessagePublisher.sendMsgToSave(chatMsg);
        }

//        List<Channel> myOtherChannels = UserChannelSession.getMyOtherChannels(senderId, currentChannelId);
//        //给自己的其他在线端口发送消息
//        for (Channel c : myOtherChannels) {
//            Channel findChannel = clients.find(c.id());
//            if (findChannel != null) {
//                dataContent.setChatMsg(chatMsg);
//                String chatTimeFormat = LocalDateUtils
//                        .format(chatMsg.getChatTime(), LocalDateUtils.DATETIME_PATTERN_2);
//                dataContent.setChatTime(chatTimeFormat);
//                // 发送消息给自己的其他在线端口
//                findChannel.writeAndFlush(new TextWebSocketFrame(JsonUtils.objectToJson(dataContent)));
//            }
//        }

//        clients.writeAndFlush(new TextWebSocketFrame(currentChannelId));

        UserChannelSession.outputMulti();
    }

    /**
     * 客户端连接到服务端后（打开连接）
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel currentChannel = ctx.channel();
        String currentChannelId = currentChannel.id().asLongText();
        System.out.println("客户端建立连接，对应长id为："+currentChannelId);
        //获得客户端channel，并加入到channel组中进行管理
        clients.add(currentChannel);
    }

    /**
     * 关闭连接，移除channel
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel currentChannel = ctx.channel();
        String currentChannelId = currentChannel.id().asLongText();
        System.out.println("客户端关闭连接，对应长id为："+currentChannelId);

        //移除多余会话
        String userId = UserChannelSession.getUserIdByChannelId(currentChannelId);
        UserChannelSession.removeUselessChannels(userId, currentChannelId);
        //从channel组中移除
        clients.remove(currentChannel);

        Jedis jedis = JedisPoolUtils.getJedis();
        NettyServerNode minNode = jsonToPojo(jedis.get(userId)
                , NettyServerNode.class);
        // 当前节点的在线人数减1
        ZookeeperRegister.decrementOnlineCounts(minNode);

    }

    /**
     * 发生异常并且捕获，移除channel
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Channel currentChannel = ctx.channel();
        String currentChannelId = currentChannel.id().asLongText();
        System.out.println("发生异常并且捕获，对应长id为："+currentChannelId);
        //关闭channel
        ctx.channel().close();
        //从channel组中移除
        clients.remove(currentChannel);

        //移除多余会话
        String userId = UserChannelSession.getUserIdByChannelId(currentChannelId);
        UserChannelSession.removeUselessChannels(userId, currentChannelId);

        Jedis jedis = JedisPoolUtils.getJedis();
        NettyServerNode minNode = jsonToPojo(jedis.get(userId)
                , NettyServerNode.class);
        // 当前节点的在线人数减1
        ZookeeperRegister.decrementOnlineCounts(minNode);
    }
}
